{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 自然语言处理\n",
    "\n",
    "自然语言处理（NLP, Natural Language Processing），作为 AI 领域中最重要的分支之一，与**语言文字**这一承载人类文明的重要载体紧密相连，研究语言能力和语言应用的模型，建立计算机（算法）框架来实现这样的语言模型，并完善、评测、最终用于设计各种实用系统。\n",
    "\n",
    "下面列出一些自然语言处理领域常见任务，并按照任务类型分成五大类：\n",
    "\n",
    "- **词法分析：对自然语言进行词汇层面的分析，是NLP基础性工作**\n",
    "\n",
    "  - 分词：对没有明显边界的文本进行切分，得到词序列\n",
    "  - 词性标注：确定文本中每个词的词性。词性包括动词、名词、代词等\n",
    "  - 新词发现：找出文本中具有新形势、新意义或是新用法的词\n",
    "  - 形态分析：分析单词的形态组成，包括词干、词根、词缀等\n",
    "  - 拼写校正：找出拼写错误的词并进行纠正\n",
    "\n",
    "\n",
    "- **句子分析：对自然语言进行句子层面的分析，包括句法分析和其他句子级别的分析任务**\n",
    "  - 组块分析：标出句子中的短语块，例如名词短语，动词短语等\n",
    "  - 成分句法分析：分析句子的成分，给出一棵树由终结符和非终结符构成的句法树\n",
    "  - 依存句法分析：分析句子中词与词之间的依存关系，给一棵由词语依存关系构成的依存句法树\n",
    "  - 语种识别：给定一段文本，确定该文本属于哪个语种\n",
    "  - 句子边界检测：给没有明显句子边界的文本加边界\n",
    "\n",
    "\n",
    "- **语义分析：对给定文本进行分析和理解，形成能够表达语义的形式化表示或分布式表示**\n",
    "  - 词义消歧：对有歧义的词，确定其准确的词义\n",
    "  - 语义角色标注：标注句子中的语义角色类标，语义角色，语义角色包括施事、受事、影响等\n",
    "  - 框架语义分析：根据框架语义学的观点，对句子进行语义分析\n",
    "  - 词汇/句子/段落的向量化表示：研究词汇、句子、段落的向量化方法，向量的性质和应用\n",
    " \n",
    " \n",
    "- **信息抽取：从无结构文本中抽取结构化的信息**\n",
    "  - 命名实体识别：从文本中识别出命名实体\n",
    "  - 实体消歧：确定实体指代对象\n",
    "  - 术语抽取：从文本中确定术语\n",
    "  - 关系抽取：确定文本中两个实体之间的关系类型\n",
    "  - 事件抽取：从无结构的文本中抽取结构化事件\n",
    "  - 情感分析：对文本的主观性情绪进行提取\n",
    "  - 意图识别：对用户给定的对话内容进行分析，识别用户意图\n",
    "  \n",
    "  \n",
    "- **顶层任务：直接面向普通用户，提供自然语言处理产品服务的系统级任务，会用到多个层面的自然语言处理技术**\n",
    "  - 机器翻译：通过计算机自动化的把一种语言翻译成另外一种语言\n",
    "  - 文本摘要：对较长文本进行内容梗概的提取\n",
    "  - 问答系统：针对用户提出的问题，系统给出相应的答案\n",
    "  - 对话系统：能够与用户进行聊天对话，从对话中捕获用户的意图，并分析执行\n",
    "  - 阅读理解：机器阅读完一篇文章后，给定一些文章相关问题，机器能够回答\n",
    "  - 自动文章分类：给定一篇文章，对文章的质量进行打分或分类\n",
    "  \n",
    "## 神经网络语言模型发展\n",
    "\n",
    "### 语言模型 \n",
    "\n",
    "NLP里面做预训练一般的选择是用语言模型任务来做。\n",
    "\n",
    "语言模型就是用来计算一个句子的概率的模型，也就是判断一句话是否是人说出来的自然句子的概率。\n",
    "\n",
    "语言模型包括文法语言模型和统计语言模型，我们一般使用统计语言模型。常见的统计语言模型有N元文法模型（N-gram Model）。\n",
    "\n",
    "语言模型可以捕捉到词的上下文信息。\n",
    "\n",
    "### 词向量（word embedding）\n",
    "\n",
    "词向量是为了解决自然语言处理领域的文字信息表示问题。在图像领域，使用图片的像素构成的矩阵展平成 vector 作为神经网络的输入；在语音领域，用音频频谱序列向量构成的矩阵作为神经网络的输入；在自然语言处理领域，将每个词映射成一个向量编码，作为神经网络的输入。\n",
    "\n",
    "词向量的核心问题是上下文与目标词之间的关系建模。比较热门的使用语言模型做 Word Embedding 的工具有 Word2Vec 和 Glove。\n",
    "\n",
    "### ELMO\n",
    "\n",
    "由于Word Embedding无法解决多义词问题，Word Embedding本质是一个静态的方式，不会根据上下文的改变而变化。所以引出ELMO（Embedding from Language Models），ELMO提供了一种简洁优雅的解决方案。\n",
    "\n",
    "ELMO的本质思想是：事先用语言模型学好一个单词的 Word Embedding，此时多义词无法区分，在实际使用 Word Embedding 的时候，单词已经具备了特定的上下文，这个时候可以根据上下文单词的语义去调整单词的 Word Embedding 表示，这样经过调整后的 Word Embedding 更能表达在这个上下文中的具体含义。所以 ELMO 本身是个根据当前上下文对 Word Embedding 动态调整的思路。 \n",
    "\n",
    "ELMO 采用了典型的两阶段过程，第一个阶段是利用语言模型进行预训练；第二个阶段是在做下游任务时，从预训练网络中提取对应单词的网络各层的 Word Embedding 作为新特征补充到下游任务中。\n",
    "\n",
    "### GPT\n",
    "\n",
    "GPT（Generative Pre-Training），其含义是指的生成式的预训练。\n",
    "\n",
    "与 ELMO 类似，GPT也采用两阶段过程，第一个阶段是利用语言模型进行预训练，第二阶段通过 Fine-tuning 的模式解决下游任务。\n",
    "\n",
    "GPT 的预训练虽然仍然是以语言模型作为目标任务，但是采用的是单向的语言模型，单向的含义是指：只使用单词的上文来进行预测，而抛开了下文。因此也限制了其在更多应用场景的效果，比如阅读理解这种任务，在做任务的时候是可以允许同时看到上文和下文一起做决策的。\n",
    "\n",
    "### NNLM\n",
    "\n",
    "NNLM（神经网络语言模型），通过为每个单词学习一个分布式表征来实现在连续空间上的建模。\n",
    "\n",
    "第一个 NNLM——前馈神经网络语言模型（FFNNLM）由 Bengio 等人于 2003 年提出，其性能要优于 N 元语言模型。随后，Mikolov 等人于 2010 年提出了 RNN 语言模型（RNNLM）。从那时起，NNLM 逐渐成为了主流的语言模型，并得到了迅速发展。\n",
    "\n",
    "2012 年，Sundermeyer 等人提出了长短期记忆循环神经网络语言模型（LSTM-RNNLM）用于解决学习长期依赖的问题。为了降低训练、评估以及 PPL 的开销，人们提出了各种各样的改进方案，例如分层的 Softmax、缓存模型等。近年来，为了改进 NNLM，人们引入了注意力机制（Attention），取得了显著的性能提升。\n",
    "\n",
    "### Transformer\n",
    "\n",
    "Transformer 是个叠加的自注意力机制（Self Attention）构成的深度网络，是目前NLP里最强的特征提取器。Transformer 的结构由 encoder（编码）和 decoder（解码）组成。Transformer 详见论文[《Attention is all you need》](https://arxiv.org/abs/1706.03762)。\n",
    "\n",
    "\n",
    "## 语料\n",
    "\n",
    "人工智能的核心在于数据支持。对AI有一些接触之后，我们可以知道，无论模型性能再高，都离不开大量数据作为基础。\n",
    "\n",
    "在自然语言处理领域，数据就是语料，语料的集合被称为语料库（Corpus）。语料的获取方式有以下几种：\n",
    "\n",
    "1. 业务提供的已有语料\n",
    "2. 国内外标准开放数据集\n",
    "3. 通过爬虫抓取网络数据（合法情况下）\n",
    "\n",
    "在获取语料的基础上，另一个重要的步骤是语料的预处理。语料的质量直接影响模型的训练性能，噪声很大、与任务无关的语料需要进过一系列预处理工作才可以被使用。在一个完整的中文自然语言处理工程应用中，语料预处理大概会占到50%-70%的工作量。\n",
    "\n",
    "常用的语料预处理方式包括：去除不规则符号及格式，人工去重、对齐、删除和标注等，或者规则提取内容、正则表达式匹配、根据词性和命名实体提取、编写脚本或者代码批处理等。\n",
    "\n",
    "语料的预处理，即数据清洗，是一项工作量大且重复性高的工作，在本教学任务中不进行展开说明。\n",
    "\n",
    "下面将结合一些自然语言处理的基础任务，进一步帮助大家掌握 NLP 的关键知识点。\n",
    "\n",
    "### 进入ModelArts\n",
    "\n",
    "点击如下链接：https://www.huaweicloud.com/product/modelarts.html ， 进入ModelArts主页。点击“立即使用”按钮，输入用户名和密码登录，进入ModelArts使用页面。\n",
    "\n",
    "### 创建ModelArts Notebook\n",
    "\n",
    "下面，我们在ModelArts中创建一个Notebook开发环境，ModelArts Notebook提供网页版的Python开发环境，可以方便的编写、运行代码，并查看运行结果。\n",
    "\n",
    "第一步：在ModelArts服务主界面依次点击“开发环境”、“创建”\n",
    "\n",
    "![create_nb_create_button](./img/create_nb_create_button.png)\n",
    "\n",
    "第二步：填写notebook所需的参数：\n",
    "\n",
    "| 参数 | 说明 |\n",
    "| - - - - - | - - - - - |\n",
    "| 计费方式 | 按需计费  |\n",
    "| 名称 | 自定义名称 |\n",
    "| 工作环境 | Python3 |\n",
    "| 资源池 | 公共资源池 |\n",
    "| 类型 | GPU |\n",
    "| 规格 | [限时免费]体验规格GPU版 |\n",
    "| 存储配置 | EVS |\n",
    "| 磁盘规格 | 5GB |\n",
    "\n",
    "第三步：配置好Notebook参数后，点击下一步，进入Notebook信息预览。确认无误后，点击“立即创建”\n",
    "\n",
    "第四步：创建完成后，返回开发环境主界面，等待Notebook创建完毕后，打开Notebook，进行下一步操作。\n",
    "![modelarts_notebook_index](./img/modelarts_notebook_index.png)\n",
    "\n",
    "### 在ModelArts中创建开发环境\n",
    "\n",
    "接下来，我们创建一个实际的开发环境，用于后续的实验步骤。\n",
    "\n",
    "第一步：点击下图所示的“打开”按钮，进入刚刚创建的Notebook\n",
    "![enter_dev_env](img/enter_dev_env.png)\n",
    "\n",
    "第二步：创建一个Python3环境的的Notebook。点击右上角的\"New\"，然后创建TensorFlow-1.13.1开发环境。\n",
    "\n",
    "第三步：点击左上方的文件名\"Untitled\"，并输入一个与本实验相关的名称，如\"nlp_introduction\"\n",
    "![notebook_untitled_filename](./img/notebook_untitled_filename.png)\n",
    "![notebook_name_the_ipynb](./img/notebook_name_the_ipynb.png)\n",
    "\n",
    "\n",
    "### 在Notebook中编写并执行代码\n",
    "\n",
    "在Notebook中，我们输入一个简单的打印语句，然后点击上方的运行按钮，可以查看语句执行的结果：\n",
    "![run_helloworld](./img/run_helloworld.png)\n",
    "\n",
    "\n",
    "## jieba中文分词组件\n",
    "\n",
    "近年来，随着 NLP 技术的日益成熟，开源的分词工具越来越多，如Ansj、盘古分词等。\n",
    "\n",
    "在本部分中，我们选取了优秀的 Python 中文分词组件——jieba，来进行一些基础 NLP 任务演示。\n",
    "\n",
    "首先导入`jieba`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "jieba导入成功\n"
     ]
    }
   ],
   "source": [
    "# 导入jieba\n",
    "import jieba\n",
    "\n",
    "print(\"jieba导入成功\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 分词\n",
    "\n",
    "**分词**任务是中文自然语言处理的基础性工作。汉语是以字位单位，不像西方语言，词与词之间没有空格之类的标志指示词的边界。词是最小的能够独立活动的有意义的语言成分，因此分词的好坏直接对后续文本处理任务起到关键作用。\n",
    "\n",
    "中文分词示例：\n",
    "\n",
    "`\n",
    "北京 / 故宫 / 是 / 中国 / 明清 / 两代 / 的 / 皇家 / 宫殿 / ， / 旧称 / 紫禁城，位于 / 北京 / 中轴线 / 的 / 中心 / ， / 是 / 中国 / 古代 / 宫廷 / 建筑 / 之 / 精华 / 。 / 北京 / 故宫 / 占地 / 面积 / 72 / 万 / 平方米 / ， / 建筑 / 面积 / 约 / 15 / 万 / 平方米 / 。\n",
    "`\n",
    "\n",
    "\n",
    "中文分词方法： \n",
    "\n",
    "1. 基于字典、词库匹配的分词方法（基于规则） \n",
    "\n",
    "   基于字符串匹配分词，机械分词算法。将待分的字符串与一个充分大的机器词典中的词条进行匹配。分为正向匹配和逆向匹配；最大长度匹配和最小长度匹配；单纯分词和分词与标注过程相结合的一体化方法。所以常用的有：正向最大匹配，逆向最大匹配，最少切分法。\n",
    "   \n",
    "   \n",
    "2. 基于词频度统计的分词方法（基于统计） \n",
    "\n",
    "   相邻的字同时出现的次数越多，越有可能构成一个词语，对语料中的字组频度进行统计，基于词的频度统计的分词方法是一种全切分方法。\n",
    "   \n",
    "   \n",
    "3. 基于知识理解的分词方法\n",
    "\n",
    "   该方法主要基于句法、语法分析，并结合语义分析，通过对上下文内容所提供信息的分析对词进行定界。由于汉语语言知识的笼统、复杂性，难以将各种语言信息组织成机器可直接读取的形式。因此目前基于知识的分词系统还处在试验阶段。\n",
    "\n",
    "**jieba 分词**结合了基于规则和基于统计这两类方法。\n",
    "\n",
    "首先基于前缀词典进行词图扫描，前缀词典是指词典中的词按照前缀包含的顺序排列，例如词典中出现了“广”，之后以“广”开头的词都会出现在这一部分，例如“广东”，进而会出现“广东省”，从而形成一种层级包含结构。\n",
    "\n",
    "如果将词看作节点，词和词之间的分词符看作边，那么一种分词方案则对应着从第一个字到最后一个字的一条分词路径。\n",
    "\n",
    "因此，基于前缀词典可以快速构建包含全部可能分词结果的有向无环图，这个图中包含多条分词路径，有向是指全部的路径都始于第一个字、止于最后一个字，无环是指节点之间不构成闭环。\n",
    "\n",
    "基于标注语料，使用动态规划的方法可以找出最大概率路径，并将其作为最终的分词结果。对于未登录词，jieba 使用了基于汉字成词的 HMM（隐马尔可夫模型）模型，采用了 Viterbi（维特比）算法进行推导。\n",
    "\n",
    "jieba 分词支持三种分词模式：\n",
    "\n",
    "- 全模式：把句子中所有的可以成词的词语都扫描出来, 速度非常快，但是不能解决歧义；\n",
    "\n",
    "\n",
    "- 精确模式：试图将句子最精确地切开，适合文本分析；\n",
    "\n",
    "\n",
    "- 搜索引擎模式：在精确模式的基础上，对长词再次切分，提高召回率，适合用于搜索引擎分词。\n",
    "\n",
    "下面执行程序来查看 jieba 分词的效果，首先选择两个句子来分词"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "待分词语句1：明天我要去五棵松体育场打篮球\n",
      "\n",
      "待分词语句2：华为联手中国电信，完成了武汉火神山医院首个远程会诊平台的网络铺设和设备调试\n"
     ]
    }
   ],
   "source": [
    "# 待分词语句\n",
    "seg_text_1 = \"明天我要去五棵松体育场打篮球\"\n",
    "seg_text_2 = \"华为联手中国电信，完成了武汉火神山医院首个远程会诊平台的网络铺设和设备调试\"\n",
    "\n",
    "print(\"待分词语句1：\" + seg_text_1)\n",
    "print(\"\\n待分词语句2：\" + seg_text_2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`jieba.cut`，当`cut_all=True`时，为全模式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Building prefix dict from the default dictionary ...\n",
      "Loading model from cache /tmp/jieba.cache\n",
      "Loading model cost 0.655 seconds.\n",
      "Prefix dict has been built succesfully.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "全模式1：明天/ 我/ 要/ 去/ 五棵松/ 体育/ 体育场/ 打篮球/ 篮球\n",
      "\n",
      "全模式2：华为/ 联手/ 手中/ 中国/ 中国电信/ 国电/ 电信/ / / 完成/ 了/ 武汉/ 火神/ 神山/ 医院/ 首个/ 远程/ 会诊/ 平台/ 的/ 网络/ 铺设/ 和/ 设备/ 调试\n"
     ]
    }
   ],
   "source": [
    "# 全模式\n",
    "seg_1_1 = jieba.cut(seg_text_1, cut_all=True)\n",
    "seg_1_2 = jieba.cut(seg_text_2, cut_all=True)\n",
    "\n",
    "print(\"全模式1：\" + \"/ \".join(seg_1_1))  \n",
    "print(\"\\n全模式2：\" + \"/ \".join(seg_1_2))  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`jieba.cut`，当`cut_all=False`时，为精确模式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "精确模式1：明天/ 我要/ 去/ 五棵松/ 体育场/ 打篮球\n",
      "\n",
      "精确模式2：华为/ 联手/ 中国电信/ ，/ 完成/ 了/ 武汉/ 火神/ 山/ 医院/ 首个/ 远程/ 会诊/ 平台/ 的/ 网络/ 铺设/ 和/ 设备/ 调试\n"
     ]
    }
   ],
   "source": [
    "# 精确模式\n",
    "seg_2_1 = jieba.cut(seg_text_1, cut_all=False)\n",
    "seg_2_2 = jieba.cut(seg_text_2, cut_all=False)\n",
    "\n",
    "print(\"精确模式1：\" + \"/ \".join(seg_2_1))  \n",
    "print(\"\\n精确模式2：\" + \"/ \".join(seg_2_2))  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`jieba.cut_for_search`为搜索引擎模式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "搜索引擎模式1：明天/ 我要/ 去/ 五棵松/ 体育/ 体育场/ 篮球/ 打篮球\n",
      "\n",
      "搜索引擎模式2：华为/ 联手/ 中国/ 国电/ 电信/ 中国电信/ ，/ 完成/ 了/ 武汉/ 火神/ 山/ 医院/ 首个/ 远程/ 会诊/ 平台/ 的/ 网络/ 铺设/ 和/ 设备/ 调试\n"
     ]
    }
   ],
   "source": [
    "# 搜索引擎模式\n",
    "seg_3_1 = jieba.cut_for_search(seg_text_1)  \n",
    "seg_3_2 = jieba.cut_for_search(seg_text_2)  \n",
    "\n",
    "print(\"搜索引擎模式1：\" + \"/ \".join(seg_3_1))\n",
    "print(\"\\n搜索引擎模式2：\" + \"/ \".join(seg_3_2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "以上，\n",
    "- `jieba.cut` 以及 `jieba.cut_for_search` 返回的结构都是一个可迭代的生成器 generator，可以使用 for 循环来获得分词后得到的每一个词语\n",
    "- `jieba.lcut` 以及 `jieba.lcut_for_search` 可以直接返回 list\n",
    "\n",
    "如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "返回全模式列表1：['明天', '我', '要', '去', '五棵松', '体育', '体育场', '打篮球', '篮球']\n",
      "\n",
      "返回全模式列表2：['华为', '联手', '手中', '中国', '中国电信', '国电', '电信', '', '', '完成', '了', '武汉', '火神', '神山', '医院', '首个', '远程', '会诊', '平台', '的', '网络', '铺设', '和', '设备', '调试']\n"
     ]
    }
   ],
   "source": [
    "# 返回全模式列表\n",
    "seg_4_1 = jieba.lcut(seg_text_1, cut_all=True)\n",
    "seg_4_2 = jieba.lcut(seg_text_2, cut_all=True)\n",
    "\n",
    "print(\"返回全模式列表1：{0}\".format(seg_4_1))\n",
    "print(\"\\n返回全模式列表2：{0}\".format(seg_4_2))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "返回精确模式列表1：['明天', '我要', '去', '五棵松', '体育场', '打篮球']\n",
      "\n",
      "返回精确模式列表2：['华为', '联手', '中国电信', '，', '完成', '了', '武汉', '火神', '山', '医院', '首个', '远程', '会诊', '平台', '的', '网络', '铺设', '和', '设备', '调试']\n"
     ]
    }
   ],
   "source": [
    "# 返回精确模式列表\n",
    "seg_5_1 = jieba.lcut(seg_text_1, cut_all=False)\n",
    "seg_5_2 = jieba.lcut(seg_text_2, cut_all=False)\n",
    "\n",
    "print(\"返回精确模式列表1：{0}\".format(seg_5_1))\n",
    "print(\"\\n返回精确模式列表2：{0}\".format(seg_5_2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "jiaba 分词不仅可以支持三种分词模式，而且可以支持繁体分词和自定义词典。感兴趣的同学可以自行探索。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 关键词提取\n",
    "\n",
    "关键词提取就是从文本里面把跟这篇文章意义最相关的一些词语抽取出来，在文献检索、自动文摘、文本分类等方面有着重要的应用。\n",
    "\n",
    "目前比较常用的关键词提取算法都是基于无监督算法，如：TF-IDF 算法，TextRank 算法和主题模型算法（包括LSA，LSI，LDA等）。\n",
    "\n",
    "**jieba** 提供了两种关键词提取方法，分别基于 TF-IDF 算法和 TextRank 算法。\n",
    "\n",
    "首先从`jieba`中引入关键词提取模块`analyse`，输入原始文本"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "文本： 天安门广场，位于北京市中心，地处北京市东城区东长安街，北起天安门，南至正阳门，东起中国国家博物馆，西至人民大会堂，南北长880米，东西宽500米，面积达44万平方米，可容纳100万人举行盛大集会，是世界上最大的城市广场。广场地面全部由经过特殊工艺技术处理的浅色花岗岩条石铺成，中央矗立着人民英雄纪念碑和庄严肃穆的毛主席纪念堂，天安门两边是劳动人民文化宫和中山公园，与天安门浑然一体，共同构成天安门广场。\n"
     ]
    }
   ],
   "source": [
    "from jieba import analyse\n",
    "\n",
    "# 原始文本\n",
    "anal_text = \"天安门广场，位于北京市中心，地处北京市东城区东长安街，北起天安门，南至正阳门，东起中国国家博物馆，西至人民大会堂，南北长880米，东西宽500米，面积达44万平方米，可容纳100万人举行盛大集会，是世界上最大的城市广场。广场地面全部由经过特殊工艺技术处理的浅色花岗岩条石铺成，中央矗立着人民英雄纪念碑和庄严肃穆的毛主席纪念堂，天安门两边是劳动人民文化宫和中山公园，与天安门浑然一体，共同构成天安门广场。\"\n",
    "print(\"文本：\", anal_text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### TF-IDF 算法\n",
    "\n",
    "TF-IDF（Term Frequency-Inverse Document Frequency, 词频-逆文件频率）是一种统计方法，用以评估一个词语对于一个文件集或一个语料库中的一份文件的重要程度，其原理为：一个词语在一篇文章中出现次数越多，同时在所有文档中出现次数越少，越能够代表该文章。\n",
    "\n",
    "计算公式为：**TF-IDF = TF·IDF**\n",
    "\n",
    "其中：\n",
    "\n",
    "- TF（term frequency）：词频，某一个给定的词语在该文件中出现的次数，计算公式：\n",
    "\n",
    "![](./img/tf.png)\n",
    "\n",
    "- IDF（inverse document frequency）：逆文件频率，如果包含词条的文件越少，则说明词条具有很好的类别区分能力，计算公式：\n",
    "\n",
    "![](./img/idf.png)\n",
    "\n",
    "通过 `jieba.analyse.extract_tags` 方法可以基于 TF-IDF 算法进行关键词提取，\n",
    "\n",
    "其中`topK`为返回几个 TF/IDF 权重最大的关键词，默认值为 20 ；`withWeight`为是否需要返回关键词权重值。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "TF-IDF算法提取的20个关键词：\n",
      "\n",
      "天安门     0.49065833855672725\n",
      "天安门广场     0.3559928650109091\n",
      "北京市     0.2696187950192727\n",
      "广场     0.258730545708\n",
      "劳动人民文化宫     0.22016214877636364\n",
      "880     0.21735940914363636\n",
      "500     0.21735940914363636\n",
      "44     0.21735940914363636\n",
      "100     0.21735940914363636\n",
      "东长安街     0.21087441016363637\n",
      "毛主席纪念堂     0.2075594727672727\n",
      "人民英雄纪念碑     0.19653882179272728\n",
      "条石     0.19421457867454545\n",
      "中山公园     0.19350147480000002\n",
      "浑然一体     0.19215405712363637\n",
      "东城区     0.19151603312727275\n",
      "庄严肃穆     0.18916672891818181\n",
      "国家博物馆     0.18660166349454543\n",
      "浅色     0.17712353761127272\n",
      "工艺技术     0.1765640529081818\n"
     ]
    }
   ],
   "source": [
    "# 引入TF-IDF关键词抽取接口\n",
    "tfidf = analyse.extract_tags\n",
    "\n",
    "print(\"TF-IDF算法提取的20个关键词：\\n\")\n",
    "\n",
    "# 基于TF-IDF算法进行关键词抽取\n",
    "for x, w in tfidf(anal_text, withWeight=True):\n",
    "    print('%s     %s' % (x, w))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### TextRank 算法\n",
    "\n",
    "`TextRank`是另一种关键词提取算法，其基本思想来源于`PageRank`算法，通过把文本分割成若干组成单元（单词、句子）并建立图模型，利用投票机制对文本中的重要成分进行排序，仅利用单篇文档本身的信息即可实现关键词提取、文摘。\n",
    "\n",
    "`TextRank`原理详细内容参见论文[《TextRank: Bringing Order into Texts》](http://web.eecs.umich.edu/~mihalcea/papers/mihalcea.emnlp04.pdf) \n",
    "\n",
    "通过`jieba.analyse.textrank`方法可以使用基于`TextRank`算法的关键词提取\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "TextRank 算法提取的20个关键词：\n",
      "\n",
      "天安门    1.0\n",
      "北京市    0.984195698973592\n",
      "广场    0.9505119244156767\n",
      "中心    0.624407755383316\n",
      "条石    0.5781642202663091\n",
      "天安门广场    0.5529255526579524\n",
      "城市    0.5478420195704256\n",
      "浅色    0.5258944293128256\n",
      "处理    0.5204568301010987\n",
      "全部    0.508037676207519\n",
      "地面    0.49265910927575324\n",
      "集会    0.4891675988744326\n",
      "正阳门    0.4840392707929205\n",
      "工艺技术    0.4792956841186776\n",
      "中央    0.47392286455542104\n",
      "东长安街    0.45514699388551993\n",
      "东城区    0.4028578581107597\n",
      "位于    0.39295290774038244\n",
      "世界    0.3929341207023511\n",
      "构成    0.3544141863557638\n"
     ]
    }
   ],
   "source": [
    "# 引入TextRank关键词抽取接口\n",
    "textrank = analyse.textrank\n",
    "\n",
    "print(\"TextRank 算法提取的20个关键词：\\n\")\n",
    "\n",
    "# 基于TextRank算法进行关键词抽取\n",
    "for x, w in textrank(anal_text, withWeight=True):\n",
    "    print('%s    %s' % (x, w))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以看到，基于不同的算法提取关键词的结果不同，在实际情况中可以根据需求选取合适的算法。\n",
    "\n",
    "### 词性标注\n",
    "\n",
    "词性标注（POS tagging，Part-Of-Speech tagging）也被称为语法标注，是自然语言处理中一项非常重要的基础性工作。\n",
    "\n",
    "词性标注是各类基于文本的机器学习任务，例如语义分析和指代消解的预处理步骤。\n",
    "\n",
    "词性是词汇基本的语法范畴，通常也称为词类，主要用来描述一个词在上下文的作用。例如，描述一个概念的词就是名词，在下文引用这个名词的词就是代词。有的词性经常会出现一些新的词，例如名词，这样的词性叫做开放式词性。另外一些词性中的词比较固定，例如代词，这样的词性叫做封闭式词性。因为存在一个词对应多个词性的现象，所以给词准确地标注词性并不是很容易。例如，“改革”在“中国开始对计划经济体制进行改革”这句话中是一个动词，但是在“医药卫生改革中的经济问题”这个句子中是一个名词。把这个问题抽象出来，就是已知单词序列，给每个单词标注词性。\n",
    "\n",
    "目前采用的词性标注方法主要有基于统计模型的标注方法、基于规则的标注方法、统计方法与规则方法相结合的方法、基于有限状态转换机的标注方法和基于神经网络的词性标注方法。\n",
    "\n",
    "不同的语言有不同的词性标注集。为了方便指明词的词性，可以给每个词性编码，可以具体参考《ICTCLAS 汉语词性标注集》，示例如下：\n",
    "\n",
    "- a —— 形容词\n",
    "- b —— 区别词\n",
    "- c —— 连词\n",
    "- d —— 副词\n",
    "- e —— 叹词\n",
    "- g —— 语素字\n",
    "- h —— 前接成分\n",
    "- i —— 成语\n",
    "- j —— 简称\n",
    "- k —— 后接成分\n",
    "- m —— 数词\n",
    "- n —— 普通名词\n",
    "- nd —— 方位名词\n",
    "- nh —— 人名\n",
    "- ni —— 机构名\n",
    "- nl —— 处所名词\n",
    "- ns —— 地名\n",
    "- nt —— 时间词\n",
    "- nz —— 其他名词\n",
    "- o —— 拟声词\n",
    "- p —— 介词\n",
    "- q —— 量词\n",
    "- r —— 代词\n",
    "- u —— 助词\n",
    "- v —— 动词\n",
    "- x —— 非语素字\n",
    "\n",
    "jieba 中提供了词性标注功能，可以标注标注句子分词后每个词的词性，词性标注集采用北大计算所词性标注集，属于采用基于统计模型的标注方法\n",
    "\n",
    "下面使用 jieba 的词性标注模块`posseg`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "文本： 天安门坐落在中华人民共和国首都北京市的中心、故宫的南端，占地面积4800平方米，以杰出的建筑艺术和特殊的政治地位为世人所瞩目。\n",
      "天安门   ns\n",
      "坐落   v\n",
      "在   p\n",
      "中华人民共和国   ns\n",
      "首都   d\n",
      "北京市   ns\n",
      "的   uj\n",
      "中心   n\n",
      "、   x\n",
      "故宫   n\n",
      "的   uj\n",
      "南端   f\n",
      "，   x\n",
      "占地面积   n\n",
      "4800   m\n",
      "平方米   q\n",
      "，   x\n",
      "以   p\n",
      "杰出   a\n",
      "的   uj\n",
      "建筑   n\n",
      "艺术   n\n",
      "和   c\n",
      "特殊   a\n",
      "的   uj\n",
      "政治   n\n",
      "地位   n\n",
      "为   p\n",
      "世人   n\n",
      "所   c\n",
      "瞩目   v\n",
      "。   x\n"
     ]
    }
   ],
   "source": [
    "import jieba.posseg as pseg\n",
    "\n",
    "pseg_text = \"天安门坐落在中华人民共和国首都北京市的中心、故宫的南端，占地面积4800平方米，以杰出的建筑艺术和特殊的政治地位为世人所瞩目。\"\n",
    "\n",
    "print(\"文本：\", pseg_text)\n",
    "\n",
    "words = pseg.cut(pseg_text)\n",
    "\n",
    "for word, flag in words:\n",
    "    print(\"{}   {}\".format(word, flag))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "以上，使用 jieba 分词工具，进行了几个基础 NLP 任务：分词、关键词提取、词性标注。\n",
    "\n",
    "相信大家对自然语言处理任务有了一定的了解。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "TensorFlow-1.13.1",
   "language": "python",
   "name": "tensorflow-1.13.1"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
